package com.allen.app.common.utils;

import cn.hutool.core.util.RandomUtil;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;
import org.slf4j.MDC;

/**
 * Description: 线程池
 *
 * @author Limiaojun
 * @date 2019/7/11 17:42
 **/
public class ThreadManager {

    private static ThreadPool mThreadPool;

    /**
     * 获取单例的线程池对象.
     *
     * @return com.lean.common.ThreadManager.ThreadPool
     * @author Limiaojun
     * @date 2019/7/11 17:29
     */
    public static ThreadPool getThreadPool() {
        if (mThreadPool == null) {
            synchronized (ThreadManager.class) {
                if (mThreadPool == null) {
                    // 获取处理器数量
                    int cpuNum = Runtime.getRuntime().availableProcessors();
                    // 根据cpu数量,计算出合理的线程并发数
                    int threadNum = cpuNum * 2 + 1;
                    System.out.println("cpu num:" + cpuNum);
                    mThreadPool = new ThreadPool(threadNum, threadNum, 0L);
                }
            }
        }

        return mThreadPool;
    }

    /**
     * 获取单例的线程池对象 根据传入数量初始化.
     *
     * @return com.lean.common.ThreadManager.ThreadPool
     * @author Limiaojun
     * @date 2019/7/11 17:29
     */
    public static ThreadPool getThreadPool(int threadNum) {
        if (mThreadPool == null) {
            synchronized (ThreadManager.class) {
                if (mThreadPool == null) {
                    mThreadPool = new ThreadPool(threadNum, threadNum, 0L);
                }
            }
        }

        return mThreadPool;
    }


    public static class ThreadPool {

        private ThreadPoolExecutor mExecutor;

        private int corePoolSize;
        private int maximumPoolSize;
        private long keepAliveTime;

        private ThreadPool(int corePoolSize, int maximumPoolSize,
            long keepAliveTime) {
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.keepAliveTime = keepAliveTime;
            initExecutor();
        }

        public void execute(Runnable runnable) {
            if (runnable == null) {
                return;
            }
            mExecutor.execute(buildRunnable(runnable));
        }

        private void initExecutor() {
            if (mExecutor == null) {
                // 核心线程数
                mExecutor = new ThreadPoolExecutor(corePoolSize,
                    // 最大线程数
                    maximumPoolSize,
                    // 闲置线程存活时间
                    keepAliveTime,
                    // 时间单位
                    TimeUnit.MILLISECONDS,
                    // 线程队列
                    new LinkedBlockingDeque<Runnable>(),
                    // 线程工厂
                    Executors.defaultThreadFactory(),
                    // 队列已满,而且当前线程数已经超过最大线程数时的异常处理策略
                    new AbortPolicy()
                );
            }
        }


        /**
         * 从线程队列中移除对象.
         *
         * @param runnable
         * @return void
         * @author Limiaojun
         * @date 2019/7/11 17:40
         */
        public void cancel(Runnable runnable) {
            if (mExecutor != null) {
                mExecutor.getQueue().remove(runnable);
            }
        }

        /**
        * 停止线程.
        * 
        * @return void 
        * @author Limiaojun
        * @date 2019/7/11 17:58
        */ 
        public void shutdown() {
            mExecutor.shutdown();
        }

        public Future<?> submit(Runnable task){
            return mExecutor.submit(buildRunnable(task));
        }

        public <T> Future<T> submit(Callable<T> task) {
            return mExecutor.submit(buildCallable(task));
        }

        public <T> Future<T> submit(Runnable task, T result) {
            return mExecutor.submit(buildRunnable(task),result);
        }

        private Runnable buildRunnable(Runnable runnable){
            String logId = CheckObjectUtils.isNull(MDC.get("logId")) ? RandomUtil.randomNumbers(11) : MDC.get("logId");
            //封装logId
            return () -> {
                MDC.put("logId", logId);
                runnable.run();
            };
        }
        private <T> Callable<T> buildCallable(Callable<T> runnable){
            String logId = CheckObjectUtils.isNull(MDC.get("logId")) ? RandomUtil.randomNumbers(11) : MDC.get("logId");
            return () -> {
                //封装logId
                MDC.put("logId", logId);
                return runnable.call();
            };
        }
    }
}

